﻿using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

public class MathTweenerIndicator : EditorWindow
{
    public enum CurveTypeEnum { Sin, Cos, ACos, Tan, ATan, SinCos1, SinCos2, SinCos3 }

    float mNumberScale = 0.01f;
    float mYScale = 10f;
    float mOffset = 0f;
    bool mToggleSimulation;
    CurveTypeEnum mCurveType;


    [MenuItem("Hont Tools/Utility/Math Tweener Indicator")]
    public static void EditorGUITestFunc()
    {
        GetWindow(typeof(MathTweenerIndicator));
    }

    void Update()
    {
        Repaint();
    }

    void OnGUI()
    {
        if (Event.current.isScrollWheel)
        {
            mNumberScale += Event.current.delta.y * 0.0001f;
            mNumberScale = Mathf.Max(0, mNumberScale);
        }

        if (Event.current.button == 2 && Event.current.isMouse && Event.current.type != EventType.MouseDown)
        {
            mYScale += Event.current.delta.y * 0.1f;
            mYScale = Mathf.Max(0, mYScale);

            mOffset += -Event.current.delta.x * 0.3f;
        }

        DrawMenus();

        DrawPreviewBox(new Rect(5, 20, Screen.width - 20, Screen.height - 40));

        DrawMathCurve(new Rect(5, 20, Screen.width - 20, Screen.height - 40));
    }

    void DrawMenus()
    {
        EditorGUILayout.BeginHorizontal(EditorStyles.toolbar);

        if (GUILayout.Button(string.Format("Curve Type: {0}", mCurveType), EditorStyles.toolbarDropDown))
        {
            var menu = new GenericMenu();
            menu.AddItem(new GUIContent("sin")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.Sin; ResetSetting(); }));

            menu.AddItem(new GUIContent("cos")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.Cos; ResetSetting(); }));

            menu.AddItem(new GUIContent("acos")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.ACos; ResetSetting(); }));

            menu.AddItem(new GUIContent("tan")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.Tan; ResetSetting(); }));

            menu.AddItem(new GUIContent("atan")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.ATan; ResetSetting(); }));

            menu.AddItem(new GUIContent("sin+cos 1")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.SinCos1; ResetSetting(); }));

            menu.AddItem(new GUIContent("sin+cos 2")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.SinCos2; ResetSetting(); }));

            menu.AddItem(new GUIContent("sin+cos 3")
              , false
              , new GenericMenu.MenuFunction(() => { mCurveType = CurveTypeEnum.SinCos3; ResetSetting(); }));

            menu.ShowAsContext();
        }

        EditorGUILayout.EndHorizontal();
    }

    void DrawPreviewBox(Rect rect)
    {
        var oldColor = Handles.color;
        var borderRect = new Rect(rect.x, rect.y, rect.width, rect.height - 20);
        GUI.Box(borderRect, "");

        Handles.color = Color.black;

        var yTipsPoint = borderRect.yMax;
        for (float x = rect.xMin; x < rect.xMax; x += 30f)
        {
            Handles.DrawLine(new Vector3(x + 4, borderRect.yMax - 3), new Vector3(x + 4, borderRect.yMax - 5));
            //vertical line.

            var number = ((x - rect.xMin) + mOffset) * mNumberScale;
            GUI.Label(new Rect(x, yTipsPoint, 40, 15), number.ToString("F1"));
        }

        Handles.color = oldColor;
    }

    void DrawMathCurve(Rect rect)
    {
        var oldColor = Handles.color;

        Handles.color = Color.black;
        var lastPoint = (Vector3?)null;

        GUI.BeginClip(new Rect(rect.x, rect.y, rect.width, rect.height - 30));

        for (float current = rect.xMin; current < rect.xMax; current++)
        {
            var relativeCurrent01 = (current - rect.xMin) / rect.width;

            var x = rect.x + relativeCurrent01 * rect.width;
            var step = ((current - rect.xMin) + mOffset) * mNumberScale;
            var y = rect.y + Math(step) * mYScale + rect.height * 0.3f;

            if (lastPoint != null)
                Handles.DrawLine(lastPoint.Value, new Vector3(x, y));

            lastPoint = new Vector3(x, y);
        }

        GUI.EndClip();

        Handles.color = oldColor;
    }

    float Math(float f)
    {
        switch (mCurveType)
        {
            case CurveTypeEnum.Sin:
                return Mathf.Sin(f);
            case CurveTypeEnum.Cos:
                return Mathf.Cos(f);
            case CurveTypeEnum.ACos:
                return Mathf.Acos(f);
            case CurveTypeEnum.ATan:
                return Mathf.Atan(f);
            case CurveTypeEnum.SinCos1:
                return Mathf.Sin(f * 3f) * 2f / Mathf.Cos(f);
            case CurveTypeEnum.SinCos2:
                return Mathf.Sin(f * 3f) / Mathf.Cos(f * 0.6f);
            case CurveTypeEnum.SinCos3:
                return Mathf.Sin(f * 2f) * 2f / Mathf.Cos(f * 0.5f) * 2f;
            default:
                return Mathf.Tan(f);
        }
    }

    void ResetSetting()
    {
        mYScale = 10f;
        mNumberScale = 0.01f;
        mOffset = 0f;
    }
}
